﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;

namespace Simp.Model.CoreExpressions.Sequences
{
	/// <summary>Sequence deriving from a source sequence where each element is transformed by a function.</summary>
	[ExpressionName(ExplicitSequenceExpressionName)]
	[ParameterPolicy("source", "transform")]
	[DebuggerDisplay("{ExpressionHelper.Serialize(this)}")]
	public class DerivedSequenceExpression : ISequenceExpression, IExpressionSerializer
	{
		#region Inner Types
		private class DerivedSequenceAccessor : ISequenceAccessor
		{
			private readonly ISequenceAccessor _sourceAccessor;
			private readonly CompositeExpression _transform;

			public DerivedSequenceAccessor(ISequenceAccessor sourceAccessor, CompositeExpression transform)
			{
				_sourceAccessor = sourceAccessor;
				_transform = transform;
			}

			#region ISequenceAccessor Members
			int ISequenceAccessor.Count
			{
				get { return _sourceAccessor.Count; }
			}

			IExpression ISequenceAccessor.this[int index]
			{
				get
				{
					IExpression derivedExpression = Transform(_sourceAccessor[index]);

					return ExpressionHelper.Simplify(false, derivedExpression, SimplificationMode.PerfectEquivalent);
				}
			}
			#endregion

			#region IEnumerable<IExpression> Members
			IEnumerator<IExpression> IEnumerable<IExpression>.GetEnumerator()
			{
				foreach (IExpression sourceElement in _sourceAccessor)
				{
					yield return Transform(sourceElement);
				}
			}
			#endregion

			#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<IExpression>)this).GetEnumerator();
			}
			#endregion

			private IExpression Transform(IExpression function)
			{
				BoundCallingExpression boundCall = new BoundCallingExpression(2);
				CompositeExpression composite = new CompositeExpression(boundCall, _transform, function);

				return composite;
			}
		}
		#endregion

		/// <summary>Name of the expression.</summary>
		public const string ExplicitSequenceExpressionName = "DerivedSequence";

		#region ISequenceExpression Members
		ISequenceAccessor ISequenceExpression.GetAccessor(params IExpression[] parameters)
		{
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			if (parameters.Length == 2)
			{
				IExpression source = parameters[0];
				ISequenceAccessor accessor = ExpressionHelper.GetAccessor(source);

				if (accessor != null)
				{
					CustomExpression customExpression;
					CompositeExpression transform = ExpressionHelper.TryGetComposite(parameters[1], out customExpression);

					if (transform != null && customExpression.IsLambda)
					{
						return new DerivedSequenceAccessor(accessor, transform);
					}
				}
			}

			//	Can't access
			return null;
		}
		#endregion

		#region IExpressionSerializer Members
		void IExpressionSerializer.Serialize(AppendOnlyStringBuilder builder, params IExpression[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			if (parameters.Length == 2)
			{
				IExpression source = parameters[0];
				IExpression transform = parameters[1];

				builder.Append("{ ");
				ExpressionHelper.Serialize(builder, source, parameters);
				builder.Append(" | ");
				ExpressionHelper.Serialize(builder, transform, parameters);
				builder.Append(" }");
			}
		}
		#endregion
	}
}